package com.doudou.oauth.token;

import com.doudou.oauth.entity.AuthUserDto;
import com.doudou.oauth.service.AppUserRoleService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: 傻男人
 * @Date: 2020/6/10 9:51
 * @Version: 1.0
 * @Description: 授权码-token授权器
 */
public class SimpleAuthorizationCodeTokenGranter extends AuthorizationCodeTokenGranter {

    private final AuthorizationCodeServices authorizationCodeServices;

    private final AppUserRoleService appUserRoleService;

    public SimpleAuthorizationCodeTokenGranter(AppUserRoleService appUserRoleService, AuthorizationServerTokenServices tokenServices, AuthorizationCodeServices authorizationCodeServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
        super(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory);
        this.authorizationCodeServices = authorizationCodeServices;
        this.appUserRoleService = appUserRoleService;
    }



    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

        Map<String, String> parameters = tokenRequest.getRequestParameters();
        String authorizationCode = parameters.get("code");
        String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI);

        if (authorizationCode == null) {
            throw new InvalidRequestException("code 参数缺失");
        }

        OAuth2Authentication storedAuth = this.authorizationCodeServices.consumeAuthorizationCode(authorizationCode);
        if (storedAuth == null) {
            throw new InvalidGrantException("授权码失效: " + authorizationCode);
        }

        OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request();
//       取消REDIRECT_URI参数检查
//        // https://jira.springsource.org/browse/SECOAUTH-333
//        // This might be null, if the authorization was done without the redirect_uri parameter
//        String redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get(
//                OAuth2Utils.REDIRECT_URI);
//
//        if ((redirectUri != null || redirectUriApprovalParameter != null)
//                && !pendingOAuth2Request.getRedirectUri().equals(redirectUri)) {
//            throw new RedirectMismatchException("Redirect URI mismatch.");
//        }

        String pendingClientId = pendingOAuth2Request.getClientId();
        String clientId = tokenRequest.getClientId();
        if (clientId != null && !clientId.equals(pendingClientId)) {
            // just a sanity check.
            throw new InvalidClientException("Client ID mismatch");
        }

        // Secret is not required in the authorization request, so it won't be available
        // in the pendingAuthorizationRequest. We do want to check that a secret is provided
        // in the token request, but that happens elsewhere.

        Map<String, String> combinedParameters = new HashMap<String, String>(pendingOAuth2Request
                .getRequestParameters());
        // Combine the parameters adding the new ones last so they override if there are any clashes
        combinedParameters.putAll(parameters);

        // Make a new stored request with the combined parameters
        OAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters);
        Authentication userAuth = userRoleAuth(storedAuth.getUserAuthentication(),finalStoredOAuth2Request);
        return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);
    }

    private Authentication userRoleAuth(Authentication userAuth , OAuth2Request oAuth2Request){
        if(!(userAuth instanceof UsernamePasswordAuthenticationToken)){
            return userAuth;
        }
        UsernamePasswordAuthenticationToken old = (UsernamePasswordAuthenticationToken)userAuth;

        AuthUserDto oldUser = (AuthUserDto) old.getPrincipal();

        String appId = oAuth2Request.getClientId();
        Collection<? extends GrantedAuthority> roles = loadRole(appId,oldUser.getUsername());
        User user =new User(oldUser.getUsername(),
                oldUser.getPassword(),
                roles);
        UsernamePasswordAuthenticationToken auth =
                new UsernamePasswordAuthenticationToken(user,old.getCredentials(),roles);
        return auth;
    }

    Collection<? extends GrantedAuthority> loadRole(String appId,String userName){
        return  appUserRoleService.loadRole(appId,userName);
    }
}
